package cn.getech.data.development.utils

import java.lang.reflect.Method
import java.util
import java.util.UUID

import scala.reflect.runtime.universe
import scala.tools.reflect.ToolBox

/**
 * 动态创建对象
 * ClassCreateUtils(Array("var userid: String = null", "var action: String = null"))
 */
object ReflectClassUtils {

  case class ClassInfo(clazz: Class[_], instance: Any, defaultMethod: Method, methods: Map[String, Method]) {
    def invoke[T](args: Object*): T = {
      defaultMethod.invoke(instance, args: _*).asInstanceOf[T]
    }
  }

  /**
   *  ClassInfo里包含了对象信息
   * @param parameter 参数s
   * @param func 方法
   * @return ClassInfo
   */
  def apply(parameter: Array[String], func: String = ""): ClassInfo = synchronized {
    var clazz = clazzs.get(func)
    if (clazz == null) {
      val (className, classBody) = wrapClass(parameter, func)
      val zz = compile(prepareScala(className, classBody))
      val defaultMethod = zz.getDeclaredMethods.head
      val methods = zz.getDeclaredMethods
      clazz = ClassInfo(
        zz,
        zz.newInstance(),
        defaultMethod,
        methods = methods.map { m => (m.getName, m) }.toMap
      )
      clazzs.put(func, clazz)
    }
    clazz
  }

  private val clazzs = new util.HashMap[String, ClassInfo]()
  private val classLoader = scala.reflect.runtime.universe.getClass.getClassLoader
  private val toolBox = universe.runtimeMirror(classLoader).mkToolBox()

  private def compile(src: String): Class[_] = {
    val tree = toolBox.parse(src)
    toolBox.compile(tree).apply().asInstanceOf[Class[_]]
  }

  private def prepareScala(className: String, classBody: String): String = {
    classBody + "\n" + s"scala.reflect.classTag[$className].runtimeClass"
  }

  private def wrapClass(parameter: Array[String], function: String): (String, String) = {
    val className = s"dynamic_class_${UUID.randomUUID().toString.replaceAll("-", "")}"
    val classBody =
      s"""
         |class $className extends Serializable {
         |  ${parameter.mkString("\n")}
         |  $function
         |}
            """.stripMargin
    (className, classBody)
  }
}

